<template>
  <Upload
    v-model:file-list="files"
    :limit="limit"
    :disabled="disabled"
    :before-upload="onBeforeUpload"
    :accept="accept"
    :customRequest="customRequest"
    :show-upload-list="{
      showRemoveIcon: true,
      showDownloadIcon: true,
    }"
    @download="downloadFile"
    @change="fileChange"
    :headers="{
      Authorization: 'Bearer ' + getToken(),
    }"
  >
    <a-button
      v-if="files.length < limit && listType === 'list' && !disabled"
      :disabled="disabled"
      ><UploadOutlined />上传</a-button
    >
    <div v-if="files.length < limit && listType !== 'list'">
      <span>+</span>
    </div>
    <template #downloadIcon>下载</template>
    <template #removeIcon>删除</template>
  </Upload>
</template>

<script setup>
import { ref, onMounted, watch } from 'vue';
import { Upload } from 'ant-design-vue';
import { message } from 'ant-design-vue/es';
import { getToken } from '@/utils/auth';
import {
  breakpointUpload,
  getGroupId,
  add,
  del,
  list,
  download,
  checkFile,
} from '@/api/sys/upload';
import * as _ from 'lodash-es';
import SparkMD5 from 'spark-md5';
import { UploadOutlined } from '@ant-design/icons-vue';
import setting from '@/settings/projectSetting';
const CHANGE_CHECK_KEY = '@@';
// 已上传数据
const files = ref([]);
const props = defineProps({
  msg: {
    type: String,
    default: '',
  },
  value: {
    type: String,
    default: '',
  },
  limit: {
    type: Number,
    default: 5,
  },
  limitSize: {
    type: Number,
    default: 10000,
  },
  listType: {
    type: String,
    default: 'list',
  },
  disabled: {
    type: Boolean,
    default: false,
  },
  accept: {
    type: String,
  },
  chunksSize: {
    type: Number,
    // 默认分片大小5M
    default: 5242880,
  },
});
const groupId = ref(props.value);
// 备份分组id
// const isFirstLoad = ref(true);
watch(
  () => props.value,
  (v) => {
    groupId.value = v;
    if (v) {
      // if (v.indexOf(CHANGE_CHECK_KEY) > -1) {
      //   groupId.value = v.replace(CHANGE_CHECK_KEY, '');
      //   console.log(groupId.value, 'ddddd');
      // } else {
      getFileList();
      // }
    } else {
      files.value = [];
      genGroupId();
    }
  },
  {
    deep: true,
  }
);
const emit = defineEmits(['update:value', 'change']);
onMounted(() => {
  setGroupId();
});
const genGroupId = () => {
  getGroupId().then((res) => {
    groupId.value = res;
    console.log('获取组id', res);
  });
};
const setGroupId = () => {
  if (groupId.value) {
    getFileList();
  } else {
    genGroupId();
  }
};
/**
 * 分片获取md5值
 * @param {*} file 文件对象
 * @param {*} processCallback 进度修改回调
 * @param {Number} chunkSize 分片大小, 默认5M
 * @returns {string} md5
 */
const md5WithChunk = (file, processCallback, chunkSize) => {
  return new Promise((resolve, reject) => {
    const chunkList = [];
    const blobSlice =
      File.prototype.slice ||
      File.prototype.mozSlice ||
      File.prototype.webkitSlice;
    // 总分片数量
    let chunks = Math.ceil(file.size / chunkSize);
    // 当前分片索引
    let currentChunk = 0;
    let spark = new SparkMD5.ArrayBuffer();
    let fileReader = new FileReader();
    fileReader.onerror = reject;
    fileReader.onload = (e) => {
      processCallback(parseInt((currentChunk / chunks) * 100));
      spark.append(e.target.result);
      currentChunk++;
      if (currentChunk < chunks) {
        loadNext();
      } else {
        processCallback(parseInt((currentChunk / chunks) * 100));
        resolve({ md5: spark.end(), chunkList });
      }
    };

    function loadNext() {
      let start = currentChunk * chunkSize;
      let end = start + chunkSize >= file.size ? file.size : start + chunkSize;
      // 切片文件
      const sliceFile = blobSlice.call(file, start, end);
      const { name, size, uid, type, lastModifiedDate } = file;
      console.log(currentChunk, start, end);
      chunkList.push({
        id: uid,
        name, // 此处用md5代替 避免文件名相同 md
        size,
        uid,
        type,
        chunks,
        chunk: currentChunk,
        md5: '',
        lastModifiedDate,
        file: sliceFile,
      });
      fileReader.readAsArrayBuffer(sliceFile);
    }
    loadNext();
  });
};

const customRequest = async (params) => {
  const { file, onSuccess, onError } = params;
  const { md5, chunkList } = await md5WithChunk(
    params.file,
    (v) => console.log(v),
    props.chunksSize
  );
  const res = await checkFile(md5, params.file.name).catch((error) => {
    message.error('文件上传失败,请检查文件名中是否含有[]等特殊字符');
    console.error(error, params);
    onError(file, error);
  });
  if (!res) return;
  const { code } = res;
  // 200代表文件完整上传到服务端了
  if (code == '200') {
    const addResult = await addFileData(file, md5).catch((error) => {
      console.log('error', error);
      onError(file, error);
    });

    if (addResult) {
      onSuccess(addResult, file);
    }

    return;
  }
  // 404代表服务端没有上传过这个文件
  if (code == '404') {
    chunkList.forEach((c) => {
      c.md5 = md5;
      c.name = md5 + c.name;
    });
    doUpload(chunkList, params, md5);
  }
  if (code == 206) {
    console.log('部分上传');
  }
};
const allWithProgress = (uploadRequests, callback) => {
  let index = 0;
  uploadRequests.forEach((request) => {
    request.then(() => {
      index++;
      const progress = (index * 100) / uploadRequests.length;
      callback(progress);
    });
  });
  return Promise.all(uploadRequests);
};
const doUpload = async (chunkList, params, md5) => {
  const { onSuccess, onError, file } = params;
  const promises = [];
  for (const e of chunkList) {
    console.log(e);
    promises.push(breakpointUpload(e));
  }
  const res = await allWithProgress(promises, (process) => {
    console.log(process, params, 'ppp');
    params.onProgress({ percent: process });
  });
  const ok = res.filter((r) => r.result == 201);
  if (ok.length) {
    await addFileData(file, md5);
    onSuccess(res, file);
  }
};
const addFileData = (file, md5) => {
  return new Promise((resolve, reject) => {
    add({
      fileGroup: groupId.value,
      fileOriginName: file.name,
      fileSizeKb: file.size / 1024,
      fileObjectName: md5,
    })
      .then((res) => {
        // 修改原始file originFileObj
        file.id = res.id;
        file.url = `${setting.fileUrl}/preview/${file.id}`;
        resolve(true);
      })
      .catch((error) => reject(error));
  });
};
const getFileList = () => {
  list(groupId.value).then((res) => {
    console.log('res', res);
    res.forEach((e) => {
      e.url = `${setting.fileUrl}/preview/${e.id}`;
      e.name = e.fileOriginName;
      e.status = 'done';
      e.uid = e.id;
    });
    files.value = res;
  });
};

const removeFile = (id) => {
  del(id).then(() => {
    message.success('删除成功');
  });
};
const change = (v) => {
  console.log('触发 change', v);
  if (v.indexOf(CHANGE_CHECK_KEY) > -1) {
    v = v.replace(CHANGE_CHECK_KEY, '');
  }
  emit('change', v);
  emit('update:value', v);
};

const fileChange = ({ file }) => {
  const { status } = file;
  // 上传完成
  if (status === 'done') {
    message.success('上传成功');
  } else if (status === 'removed') {
    removeFile(file.id);
  } else if (status === 'error') {
    files.value.pop();
  }
  console.log(files.value);

  if (files.value.length > 0) {
    // 如果是组件内部change触发则拼接一个标记 此时watch不刷新文件列表
    change(groupId.value + CHANGE_CHECK_KEY);
    // change(groupId.value);
  } else {
    change(undefined);
  }
};
/* 上传前钩子 */
const onBeforeUpload = (file) => {
  return new Promise((resolve, reject) => {
    if (file.size / 1024 / 1024 > props.limitSize) {
      message.error(`大小不能超过 ${props.limitSize}MB`);
      return false;
    }
    return resolve(true);
  });
};

const downloadFile = (file) => {
  download(file.id);
};
</script>
